home *** CD-ROM | disk | FTP | other *** search
/ Isometric Game Programming with DirectX 7.0 / Isometric Game Programming.iso / directx / dxf / samples / multimedia / directshow / baseclasses / source.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2000-11-04  |  12.2 KB  |  523 lines

  1. //------------------------------------------------------------------------------
  2. // File: Source.cpp
  3. //
  4. // Desc: DirectShow  base classes - implements CSource, which is a Quartz
  5. //       source filter 'template.'
  6. //
  7. // Copyright (c) 1992 - 2000, Microsoft Corporation.  All rights reserved.
  8. //------------------------------------------------------------------------------
  9.  
  10.  
  11. // Locking Strategy.
  12. //
  13. // Hold the filter critical section (m_pFilter->pStateLock()) to serialise
  14. // access to functions. Note that, in general, this lock may be held
  15. // by a function when the worker thread may want to hold it. Therefore
  16. // if you wish to access shared state from the worker thread you will
  17. // need to add another critical section object. The execption is during
  18. // the threads processing loop, when it is safe to get the filter critical
  19. // section from within FillBuffer().
  20.  
  21. #include <streams.h>
  22.  
  23.  
  24. //
  25. // CSource::Constructor
  26. //
  27. // Initialise the pin count for the filter. The user will create the pins in
  28. // the derived class.
  29. CSource::CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid)
  30.     : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
  31.       m_iPins(0),
  32.       m_paStreams(NULL)
  33. {
  34. }
  35.  
  36. CSource::CSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr)
  37.     : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
  38.       m_iPins(0),
  39.       m_paStreams(NULL)
  40. {
  41.     UNREFERENCED_PARAMETER(phr);
  42. }
  43.  
  44. #ifdef UNICODE
  45. CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid)
  46.     : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
  47.       m_iPins(0),
  48.       m_paStreams(NULL)
  49. {
  50. }
  51.  
  52. CSource::CSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr)
  53.     : CBaseFilter(pName, lpunk, &m_cStateLock, clsid),
  54.       m_iPins(0),
  55.       m_paStreams(NULL)
  56. {
  57.     UNREFERENCED_PARAMETER(phr);
  58. }
  59. #endif
  60.  
  61. //
  62. // CSource::Destructor
  63. //
  64. CSource::~CSource()
  65. {
  66.     /*  Free our pins and pin array */
  67.     while (m_iPins != 0) {
  68.     // deleting the pins causes them to be removed from the array...
  69.     delete m_paStreams[m_iPins - 1];
  70.     }
  71.  
  72.     ASSERT(m_paStreams == NULL);
  73. }
  74.  
  75.  
  76. //
  77. //  Add a new pin
  78. //
  79. HRESULT CSource::AddPin(CSourceStream *pStream)
  80. {
  81.     CAutoLock lock(&m_cStateLock);
  82.  
  83.     /*  Allocate space for this pin and the old ones */
  84.     CSourceStream **paStreams = new CSourceStream *[m_iPins + 1];
  85.     if (paStreams == NULL) {
  86.         return E_OUTOFMEMORY;
  87.     }
  88.     if (m_paStreams != NULL) {
  89.         CopyMemory((PVOID)paStreams, (PVOID)m_paStreams,
  90.                    m_iPins * sizeof(m_paStreams[0]));
  91.         paStreams[m_iPins] = pStream;
  92.         delete [] m_paStreams;
  93.     }
  94.     m_paStreams = paStreams;
  95.     m_paStreams[m_iPins] = pStream;
  96.     m_iPins++;
  97.     return S_OK;
  98. }
  99.  
  100. //
  101. //  Remove a pin - pStream is NOT deleted
  102. //
  103. HRESULT CSource::RemovePin(CSourceStream *pStream)
  104. {
  105.     int i;
  106.     for (i = 0; i < m_iPins; i++) {
  107.         if (m_paStreams[i] == pStream) {
  108.             if (m_iPins == 1) {
  109.                 delete [] m_paStreams;
  110.                 m_paStreams = NULL;
  111.             } else {
  112.                 /*  no need to reallocate */
  113.         while (++i < m_iPins)
  114.             m_paStreams[i - 1] = m_paStreams[i];
  115.             }
  116.             m_iPins--;
  117.             return S_OK;
  118.         }
  119.     }
  120.     return S_FALSE;
  121. }
  122.  
  123. //
  124. // FindPin
  125. //
  126. // Set *ppPin to the IPin* that has the id Id.
  127. // or to NULL if the Id cannot be matched.
  128. STDMETHODIMP CSource::FindPin(LPCWSTR Id, IPin **ppPin)
  129. {
  130.     CheckPointer(ppPin,E_POINTER);
  131.     ValidateReadWritePtr(ppPin,sizeof(IPin *));
  132.     // The -1 undoes the +1 in QueryId and ensures that totally invalid
  133.     // strings (for which WstrToInt delivers 0) give a deliver a NULL pin.
  134.     int i = WstrToInt(Id) -1;
  135.     *ppPin = GetPin(i);
  136.     if (*ppPin!=NULL){
  137.         (*ppPin)->AddRef();
  138.         return NOERROR;
  139.     } else {
  140.         return VFW_E_NOT_FOUND;
  141.     }
  142. }
  143.  
  144. //
  145. // FindPinNumber
  146. //
  147. // return the number of the pin with this IPin* or -1 if none
  148. int CSource::FindPinNumber(IPin *iPin) {
  149.     int i;
  150.     for (i=0; i<m_iPins; ++i) {
  151.         if ((IPin *)(m_paStreams[i])==iPin) {
  152.             return i;
  153.         }
  154.     }
  155.     return -1;
  156. }
  157.  
  158. //
  159. // GetPinCount
  160. //
  161. // Returns the number of pins this filter has
  162. int CSource::GetPinCount(void) {
  163.  
  164.     CAutoLock lock(&m_cStateLock);
  165.     return m_iPins;
  166. }
  167.  
  168.  
  169. //
  170. // GetPin
  171. //
  172. // Return a non-addref'd pointer to pin n
  173. // needed by CBaseFilter
  174. CBasePin *CSource::GetPin(int n) {
  175.  
  176.     CAutoLock lock(&m_cStateLock);
  177.  
  178.     // n must be in the range 0..m_iPins-1
  179.     // if m_iPins>n  && n>=0 it follows that m_iPins>0
  180.     // which is what used to be checked (i.e. checking that we have a pin)
  181.     if ((n >= 0) && (n < m_iPins)) {
  182.  
  183.         ASSERT(m_paStreams[n]);
  184.     return m_paStreams[n];
  185.     }
  186.     return NULL;
  187. }
  188.  
  189.  
  190. //
  191.  
  192.  
  193. // *
  194. // * --- CSourceStream ----
  195. // *
  196.  
  197. //
  198. // Set Id to point to a CoTaskMemAlloc'd
  199. STDMETHODIMP CSourceStream::QueryId(LPWSTR *Id) {
  200.     CheckPointer(Id,E_POINTER);
  201.     ValidateReadWritePtr(Id,sizeof(LPWSTR));
  202.  
  203.     // We give the pins id's which are 1,2,...
  204.     // FindPinNumber returns -1 for an invalid pin
  205.     int i = 1+ m_pFilter->FindPinNumber(this);
  206.     if (i<1) return VFW_E_NOT_FOUND;
  207.     *Id = (LPWSTR)CoTaskMemAlloc(8);
  208.     if (*Id==NULL) {
  209.        return E_OUTOFMEMORY;
  210.     }
  211.     IntToWstr(i, *Id);
  212.     return NOERROR;
  213. }
  214.  
  215.  
  216.  
  217. //
  218. // CSourceStream::Constructor
  219. //
  220. // increments the number of pins present on the filter
  221. CSourceStream::CSourceStream(
  222.     TCHAR *pObjectName,
  223.     HRESULT *phr,
  224.     CSource *ps,
  225.     LPCWSTR pPinName)
  226.     : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
  227.       m_pFilter(ps) {
  228.  
  229.      *phr = m_pFilter->AddPin(this);
  230. }
  231.  
  232. #ifdef UNICODE
  233. CSourceStream::CSourceStream(
  234.     char *pObjectName,
  235.     HRESULT *phr,
  236.     CSource *ps,
  237.     LPCWSTR pPinName)
  238.     : CBaseOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName),
  239.       m_pFilter(ps) {
  240.  
  241.      *phr = m_pFilter->AddPin(this);
  242. }
  243. #endif
  244. //
  245. // CSourceStream::Destructor
  246. //
  247. // Decrements the number of pins on this filter
  248. CSourceStream::~CSourceStream(void) {
  249.  
  250.      m_pFilter->RemovePin(this);
  251. }
  252.  
  253.  
  254. //
  255. // CheckMediaType
  256. //
  257. // Do we support this type? Provides the default support for 1 type.
  258. HRESULT CSourceStream::CheckMediaType(const CMediaType *pMediaType) {
  259.  
  260.     CAutoLock lock(m_pFilter->pStateLock());
  261.  
  262.     CMediaType mt;
  263.     GetMediaType(&mt);
  264.  
  265.     if (mt == *pMediaType) {
  266.         return NOERROR;
  267.     }
  268.  
  269.     return E_FAIL;
  270. }
  271.  
  272.  
  273. //
  274. // GetMediaType/3
  275. //
  276. // By default we support only one type
  277. // iPosition indexes are 0-n
  278. HRESULT CSourceStream::GetMediaType(int iPosition, CMediaType *pMediaType) {
  279.  
  280.     CAutoLock lock(m_pFilter->pStateLock());
  281.  
  282.     if (iPosition<0) {
  283.         return E_INVALIDARG;
  284.     }
  285.     if (iPosition>0) {
  286.         return VFW_S_NO_MORE_ITEMS;
  287.     }
  288.     return GetMediaType(pMediaType);
  289. }
  290.  
  291.  
  292. //
  293. // Active
  294. //
  295. // The pin is active - start up the worker thread
  296. HRESULT CSourceStream::Active(void) {
  297.  
  298.     CAutoLock lock(m_pFilter->pStateLock());
  299.  
  300.     HRESULT hr;
  301.  
  302.     if (m_pFilter->IsActive()) {
  303.     return S_FALSE;    // succeeded, but did not allocate resources (they already exist...)
  304.     }
  305.  
  306.     // do nothing if not connected - its ok not to connect to
  307.     // all pins of a source filter
  308.     if (!IsConnected()) {
  309.         return NOERROR;
  310.     }
  311.  
  312.     hr = CBaseOutputPin::Active();
  313.     if (FAILED(hr)) {
  314.         return hr;
  315.     }
  316.  
  317.     ASSERT(!ThreadExists());
  318.  
  319.     // start the thread
  320.     if (!Create()) {
  321.         return E_FAIL;
  322.     }
  323.  
  324.     // Tell thread to initialize. If OnThreadCreate Fails, so does this.
  325.     hr = Init();
  326.     if (FAILED(hr))
  327.     return hr;
  328.  
  329.     return Pause();
  330. }
  331.  
  332.  
  333. //
  334. // Inactive
  335. //
  336. // Pin is inactive - shut down the worker thread
  337. // Waits for the worker to exit before returning.
  338. HRESULT CSourceStream::Inactive(void) {
  339.  
  340.     CAutoLock lock(m_pFilter->pStateLock());
  341.  
  342.     HRESULT hr;
  343.  
  344.     // do nothing if not connected - its ok not to connect to
  345.     // all pins of a source filter
  346.     if (!IsConnected()) {
  347.         return NOERROR;
  348.     }
  349.  
  350.     // !!! need to do this before trying to stop the thread, because
  351.     // we may be stuck waiting for our own allocator!!!
  352.  
  353.     hr = CBaseOutputPin::Inactive();  // call this first to Decommit the allocator
  354.     if (FAILED(hr)) {
  355.     return hr;
  356.     }
  357.  
  358.     if (ThreadExists()) {
  359.     hr = Stop();
  360.  
  361.     if (FAILED(hr)) {
  362.         return hr;
  363.     }
  364.  
  365.     hr = Exit();
  366.     if (FAILED(hr)) {
  367.         return hr;
  368.     }
  369.  
  370.     Close();    // Wait for the thread to exit, then tidy up.
  371.     }
  372.  
  373.     // hr = CBaseOutputPin::Inactive();  // call this first to Decommit the allocator
  374.     //if (FAILED(hr)) {
  375.     //    return hr;
  376.     //}
  377.  
  378.     return NOERROR;
  379. }
  380.  
  381.  
  382. //
  383. // ThreadProc
  384. //
  385. // When this returns the thread exits
  386. // Return codes > 0 indicate an error occured
  387. DWORD CSourceStream::ThreadProc(void) {
  388.  
  389.     HRESULT hr;  // the return code from calls
  390.     Command com;
  391.  
  392.     do {
  393.     com = GetRequest();
  394.     if (com != CMD_INIT) {
  395.         DbgLog((LOG_ERROR, 1, TEXT("Thread expected init command")));
  396.         Reply((DWORD) E_UNEXPECTED);
  397.     }
  398.     } while (com != CMD_INIT);
  399.  
  400.     DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread initializing")));
  401.  
  402.     hr = OnThreadCreate(); // perform set up tasks
  403.     if (FAILED(hr)) {
  404.         DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadCreate failed. Aborting thread.")));
  405.         OnThreadDestroy();
  406.     Reply(hr);    // send failed return code from OnThreadCreate
  407.         return 1;
  408.     }
  409.  
  410.     // Initialisation suceeded
  411.     Reply(NOERROR);
  412.  
  413.     Command cmd;
  414.     do {
  415.     cmd = GetRequest();
  416.  
  417.     switch (cmd) {
  418.  
  419.     case CMD_EXIT:
  420.         Reply(NOERROR);
  421.         break;
  422.  
  423.     case CMD_RUN:
  424.         DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???")));
  425.         // !!! fall through???
  426.     
  427.     case CMD_PAUSE:
  428.         Reply(NOERROR);
  429.         DoBufferProcessingLoop();
  430.         break;
  431.  
  432.     case CMD_STOP:
  433.         Reply(NOERROR);
  434.         break;
  435.  
  436.     default:
  437.         DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd));
  438.         Reply((DWORD) E_NOTIMPL);
  439.         break;
  440.     }
  441.     } while (cmd != CMD_EXIT);
  442.  
  443.     hr = OnThreadDestroy();    // tidy up.
  444.     if (FAILED(hr)) {
  445.         DbgLog((LOG_ERROR, 1, TEXT("CSourceStream::OnThreadDestroy failed. Exiting thread.")));
  446.         return 1;
  447.     }
  448.  
  449.     DbgLog((LOG_TRACE, 1, TEXT("CSourceStream worker thread exiting")));
  450.     return 0;
  451. }
  452.  
  453.  
  454. //
  455. // DoBufferProcessingLoop
  456. //
  457. // Grabs a buffer and calls the users processing function.
  458. // Overridable, so that different delivery styles can be catered for.
  459. HRESULT CSourceStream::DoBufferProcessingLoop(void) {
  460.  
  461.     Command com;
  462.  
  463.     OnThreadStartPlay();
  464.  
  465.     do {
  466.     while (!CheckRequest(&com)) {
  467.  
  468.         IMediaSample *pSample;
  469.  
  470.         HRESULT hr = GetDeliveryBuffer(&pSample,NULL,NULL,0);
  471.         if (FAILED(hr)) {
  472.                 Sleep(1);
  473.         continue;    // go round again. Perhaps the error will go away
  474.                 // or the allocator is decommited & we will be asked to
  475.                 // exit soon.
  476.         }
  477.  
  478.         // Virtual function user will override.
  479.         hr = FillBuffer(pSample);
  480.  
  481.         if (hr == S_OK) {
  482.         hr = Deliver(pSample);
  483.                 pSample->Release();
  484.  
  485.                 // downstream filter returns S_FALSE if it wants us to
  486.                 // stop or an error if it's reporting an error.
  487.                 if(hr != S_OK)
  488.                 {
  489.                   DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr));
  490.                   return S_OK;
  491.                 }
  492.  
  493.         } else if (hr == S_FALSE) {
  494.                 // derived class wants us to stop pushing data
  495.         pSample->Release();
  496.         DeliverEndOfStream();
  497.         return S_OK;
  498.         } else {
  499.                 // derived class encountered an error
  500.                 pSample->Release();
  501.         DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr));
  502.                 DeliverEndOfStream();
  503.                 m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0);
  504.                 return hr;
  505.         }
  506.  
  507.             // all paths release the sample
  508.     }
  509.  
  510.         // For all commands sent to us there must be a Reply call!
  511.  
  512.     if (com == CMD_RUN || com == CMD_PAUSE) {
  513.         Reply(NOERROR);
  514.     } else if (com != CMD_STOP) {
  515.         Reply((DWORD) E_UNEXPECTED);
  516.         DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!")));
  517.     }
  518.     } while (com != CMD_STOP);
  519.  
  520.     return S_FALSE;
  521. }
  522.  
  523.